﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using OOP.IOC.lib.Attr;
using OOP.IOC.lib.Base;
using OOP.IOC.lib.Enums;

namespace OOP.IOC.lib
{
    public class IOCContainer2
    {
        public static Dictionary<string, ClassDefinition> Definitions = new();

        private static Dictionary<string, object> SingletonCache = new();             // 一级缓存
        private static Dictionary<string, object> EarlySingletonCache = new();        // 二级缓存
        private static Dictionary<string, Func<object>> SingletonFactoryCache = new(); // 三级缓存（延迟代理支持）

        public static void Init()
        {
            Type[] types = Assembly.Load("WpfApp1.Service").GetTypes();
            Type[] types2 = Assembly.Load("WpfApp1.DAL").GetTypes();
            List<Type> allTypes = new();
            allTypes.AddRange(types.Where(t => t.Name.Contains("Service")));
            allTypes.AddRange(types2.Where(t => t.Name.Contains("DbContext")));
            CollectionDefinition(allTypes);
            List<ClassDefinition> classDefinitions = Definitions.Values.ToList();
            CreateInstances(classDefinitions);
        }

        private static void CollectionDefinition(List<Type> dbTypes)
        {
            foreach (Type type in dbTypes)
            {
                if (type.IsInterface) continue;

                var definition = new ClassDefinition();
                var interfaces = type.GetInterfaces();
                string fullName = interfaces.FirstOrDefault(t => t.Name.Contains("Service") && !t.IsGenericType)?.FullName ?? type.FullName;

                definition.ClassName = fullName;
                definition.Type = type;

                var attr = type.GetCustomAttribute<DbSourceAttribute>();
                definition.Protocal = attr?.Protocal ?? ProtocalType.Singleton;

                Definitions[fullName] = definition;
            }
        }

        private static void CreateInstances(List<ClassDefinition> definitions)
        {
            foreach (var def in definitions)
            {
                if (def.Protocal == ProtocalType.Singleton)
                {
                    if (def.IsDbType)
                        CreateDbInfo(def, null);
                    else
                        CreateInstance(def);
                }
            }
        }

        public static object CreateInstance(ClassDefinition definition)
        {
            try
            {
                string className = definition.ClassName;
                if (SingletonCache.ContainsKey(className)) return SingletonCache[className];

                object instance = Activator.CreateInstance(definition.Type);
                EarlySingletonCache[className] = instance;

                // 准备代理工厂（用于生成代理类时调用）
                SingletonFactoryCache[className] = () => GenerateProxy(instance, definition.Type);

                var props = definition.Type.GetProperties().Where(p => p.GetCustomAttribute<AutowireAttribute>() != null);
                foreach (var prop in props)
                {
                    string depName = prop.PropertyType.FullName;
                    if (SingletonCache.ContainsKey(depName))
                    {
                        prop.SetValue(instance, SingletonCache[depName]);
                    }
                    else if (EarlySingletonCache.ContainsKey(depName))
                    {
                        prop.SetValue(instance, EarlySingletonCache[depName]);
                    }
                    else if (SingletonFactoryCache.ContainsKey(depName))
                    {
                        prop.SetValue(instance, SingletonFactoryCache[depName]());
                    }
                    else if (Definitions.TryGetValue(depName, out var depDef))
                    {
                        prop.SetValue(instance, CreateInstance(depDef));
                    }
                }

                SingletonCache[className] = instance;
                EarlySingletonCache.Remove(className);
                return instance;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                return null;
            }
        }

        private static void CreateDbInfo(ClassDefinition classDefinition, List<string> dbNames)
        {
            if (dbNames == null || dbNames.Count == 0) return;

            var dbType = classDefinition.Type;
            var attr = dbType.GetCustomAttribute<DbSourceAttribute>();
            string configId = string.IsNullOrEmpty(attr?.DbName) ? dbNames[0] : attr.DbName;

            string fullName = dbType.FullName;
            var ctor = dbType.GetConstructor(new[] { typeof(string) });
            object instance = ctor == null ? Activator.CreateInstance(dbType) : ctor.Invoke(new object[] { configId });

            SingletonCache[fullName] = instance;
        }

        public static T GetInstance<T>()
        {
            string name = typeof(T).FullName;
            if (SingletonCache.ContainsKey(name))
                return (T)SingletonCache[name];
            else if (EarlySingletonCache.ContainsKey(name))
                return (T)EarlySingletonCache[name];
            else if (SingletonFactoryCache.ContainsKey(name))
                return (T)SingletonFactoryCache[name]();
            else if (Definitions.TryGetValue(name, out var def))
                return (T)GetBean(def);

            throw new Exception($"未注册类型：{name}");
        }

        private static object GetBean(ClassDefinition def)
        {
            if (def.Protocal == ProtocalType.Singleton)
            {
                return CreateInstance(def);
            }
            else
            {
                return Activator.CreateInstance(def.Type);
            }
        }

        public static void Clear()
        {
            SingletonCache.Clear();
            EarlySingletonCache.Clear();
            SingletonFactoryCache.Clear();
        }



        /// <summary>
        /// 代理类生成钩子（待扩展）
        /// </summary>
        private static object GenerateProxy(object target, Type type)
        {
            // TODO: 在这里集成 Castle.DynamicProxy
            return target;
        }
    }
}
